// System includes
#include <QResizeEvent>
#include <QPalette>
#include <QDebug>

// Local includes
#include "yigglwidget.h"
#include "gui/yignetworkchannelview.h"
#include "graphics/yigcolorkit.h"

////////////////
// YigGLWidget
///////////////
YigGLWidget::YigGLWidget(QWidget *parent) :
    QGLWidget(parent)
{
    setAutoBufferSwap(false);
    glThread = new YigGLThread(this);
    //resize(parent->size());
    //setAttribute(Qt::WA_AcceptDrops, true);
    QGLFormat glFormat( QGL::SampleBuffers );
    glFormat.setDoubleBuffer(true);
    setFormat(glFormat);
    /*
    setAttribute(Qt::WA_TransparentForMouseEvents);
    setAttribute(Qt::WA_TranslucentBackground);
    QPalette p = palette();
    p.setColor(QPalette::Base, Qt::transparent);
    setPalette(p);
    setAutoFillBackground(false);*/
    //glFormat.setSwapInterval( 1 ); // set VSync
}

YigGLWidget::~YigGLWidget()
{
    if(glThread->isRunning())
    {
        glThread->stop();
        glThread->wait();
    }
}

void YigGLWidget::startRendering()
{
    glThread->start(QThread::LowPriority);
}

void YigGLWidget::stopRendering()
{
    glThread->stop();
    glThread->wait();
}

void YigGLWidget::resize(const QSize &size)
{
    glThread->resizeViewport(size);
    QGLWidget::resize(size);
}

QPixmap YigGLWidget::renderPixmap(int w, int h, bool useContext)
{
    lock.lockForWrite();
    glThread->pause = true;
    QPixmap pixmap = QGLWidget::renderPixmap(w, h, useContext);
    glThread->pause = false;
    lock.unlock();
    return pixmap;
}

YigGLThread* YigGLWidget::getGLThread()
{
    return glThread;
}

void YigGLWidget::resizeEvent(QResizeEvent *event)
{
    glThread->resizeViewport(event->size());
    QGLWidget::resizeEvent(event);
}

void YigGLWidget::paintEvent(QPaintEvent *)
{
    // Handled by YigGLThread
}

void YigGLWidget::closeEvent(QCloseEvent *event)
{
    stopRendering();
    QGLWidget::closeEvent(event);
}

////////////////
// YigGLThread
///////////////
YigGLThread::YigGLThread(YigGLWidget *_glWidget) :
    QThread(),
    glWidget(_glWidget),
    pause(false)
{
    currentBuffers = new QHash<int, AudioBuffer>();
    doRendering = true;
    doResize = false;
    QObject::connect(&bufferTimer, SIGNAL(timeout()), this, SLOT(prepareBufferRequest()));
    sleepTime = 31;
}

YigGLThread::~YigGLThread()
{
    foreach(int key, currentBuffers->keys())
    {
        lock.lockForWrite();
        delete[] (float*) currentBuffers->value(key).mData;
        lock.unlock();
        //free(currentBuffers->value(key)->data);
        //delete currentBuffers->value(key);
    }
    currentBuffers->clear();
    delete currentBuffers;
}

void YigGLThread::setServerOptions(ServerOptions _options)
{
    options = _options;
}

void YigGLThread::setView(YigNetworkChannelView *_view)
{
    view = _view;
}

void YigGLThread::addSynth(int index, QPointF pos)
{
    lock.lockForRead();
    if(!currentBuffers->contains(index) && !currentSynths.contains(index))
    {
        AudioBuffer scBuffer;
        scBuffer.mNumberChannels = 1;
        scBuffer.mDataByteSize = sizeof(float) * options.bufferSize * options.numAudioBusChannels;
        scBuffer.mData = (void*) malloc(sizeof(float) * options.bufferSize);
        memset(scBuffer.mData, 0, sizeof(float) * options.bufferSize);
        //SndBuf* buf = new SndBuf();
        //memset(buf, 0, sizeof(SndBuf*));
        currentBuffers->insert(index, scBuffer);
        currentSynths.insert(index, pos);
    }
    lock.unlock();
}

void YigGLThread::removeSynth(int index)
{
    if(currentBuffers->contains(index))
    {
        //lock.lockForWrite();
        delete[] (float*) currentBuffers->value(index).mData;
        currentBuffers->remove(index);
        //free(currentBuffers->value(index)->data);
        //delete currentBuffers->take(index);
        //lock.unlock();
    }

    currentSynths.remove(index);
}

void YigGLThread::setSynthPos(int index, QPointF pos)
{
    currentSynths.insert(index, pos);
}

void YigGLThread::stop()
{
    doRendering = false;
}

void YigGLThread::resizeViewport(const QSize &size)
{
    w = size.width();
    h = size.height();
    doResize = true;
}

void YigGLThread::start(Priority p)
{
    bufferTimer.start(sleepTime);
    QThread::start(p);
}

void YigGLThread::run()
{
    glWidget->makeCurrent();
    glLoadIdentity();
    // glOrtho
    //QRect rect = glWidget->parentWidget()->geometry();
    //glViewport(0, 0, rect.width(), rect.height());
    glClearColor(YigColorKit::background.redF(), YigColorKit::background.greenF(), YigColorKit::background.blueF(), 255);
    //glClearColor(0, 200, 0, 0);
    glShadeModel(GL_SMOOTH);
    glEnable(GL_DEPTH_TEST);
    glEnable (GL_LINE_SMOOTH);
    glEnable (GL_BLEND);
    //glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glHint (GL_LINE_SMOOTH_HINT, GL_FASTEST);
    glLineWidth (2.5);

    while(doRendering)
    {
        if(!pause)
        {
            if(doResize)
            {
                glViewport(0, 0, w, h);
                doResize = false;
            }

            //////////////////////////
            // Rendering begins heres

            //glClearColor(YigColorKit::background.redF(), YigColorKit::background.greenF(), YigColorKit::background.blueF(), 255);
            //glClearColor(255, 255, 255, 255);
            glClearColor(0, 200, 0, 0);
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
            //glOrtho(0, sceneRect().width(), 0, sceneRect().height(), 0, 0);
            glColor4f(255.0, 0.0, 0.0,0.75);
            //QHashIterator<QString, YigSynthGraphic*> synths(view->getSynthGraphicMap());
            QHashIterator<int, QPointF> synths(currentSynths);
            while(synths.hasNext())
            {
                synths.next();
                if(currentBuffers->contains(synths.key()))
                {
                    lock.lockForRead();
                    float* data = ((float*)currentBuffers->value(synths.key()).mData);
                    lock.unlock();
                    QPointF pos = synths.value();

                    glBegin(GL_LINE_STRIP);
                        for(int j = 0; j < options.blockSize; j++)
                        {
                            // Simple linear oscilloscope
                            glVertex2d(pos.x() + 9 + (j*0.5), pos.y() + (YigSynthGraphic::ELLIPSE_SIZE/2) - (data[j]*26));
                        }
                    glEnd();
                }

            }
            glWidget->swapBuffers();
            //glWidget->grabFrameBuffer(true);
        }
        msleep(sleepTime);
    }

}

void YigGLThread::prepareBufferRequest()
{
    if(currentBuffers->size() > 0)
    {
        emit requestAudioBuffers(currentBuffers);
    }
}
